package proxy

import (
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"sort"
	"sync"
	"time"
)

const (
	chansize = 5
)

type Proxy struct {
	Ip, Port       string
	validProxyChan chan<- Proxy // 最后有效的proxy送进的管道
	Speed          time.Duration
}

func (py *Proxy) doTest(WG *sync.WaitGroup, timeout time.Duration, log bool) {
	defer WG.Done()
	// 组装代理ip url
	py_url := py.Ip + ":" + py.Port
	proxyUrl, _ := url.Parse("http://" + py_url)
	// 组装客户端用于发送请求
	client := &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyURL(proxyUrl),
		},
		Timeout: timeout, // 设置超时时长
	}
	// 组装请求信息
	req, _ := http.NewRequest("GET", "http://www.baidu.com", nil)
	// 发送请求
	// 记录时间
	start := time.Now()
	_, err := client.Do(req)
	end := time.Now()
	speed := end.Sub(start)
	py.Speed = speed
	if err != nil {
		if log {
			fmt.Println(time.Now(), fmt.Sprintf("[Invalid Proxy] %s", py_url))
		}
		return
	}
	py.validProxyChan <- *py
	if log {
		fmt.Println(time.Now(), fmt.Sprintf("[Valid Proxy] %s", py_url))
	}
}

// 定义proxy切片和排序需要实现的接口
type ProxySlice []Proxy

func (p ProxySlice) Len() int {
	return len(p)
}

func (p ProxySlice) Less(i, j int) bool {
	return p[i].Speed < p[j].Speed
}

func (p ProxySlice) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func getOriginProxy(WorkerChan chan<- Proxy, ValidProxyChan chan Proxy, pages int, delay time.Duration, log bool) {
	defer close(WorkerChan)
	// 第一页格式不一样就跳过了
	for i := 2; i <= pages+1; i++ {
		// fmt.Printf("[getting ip] 爬取第%2d页的代理ip\n", i)
		rsp, err := http.Get(fmt.Sprintf("https://www.beesproxy.com/free/page/%d", i))
		if err != nil {
			fmt.Println("http.Get", err)
			return
		}
		body := make([]byte, 1024*50)
		var n int
		n, err = rsp.Body.Read(body)
		if err != nil {
			fmt.Println("rsp.Body.Read", err)
			return
		}
		// fmt.Println(string(body[:n]))
		reg := regexp.MustCompile(`<td>([0-9\.]+)</td>`)
		sli := reg.FindAllSubmatch(body[:n], -1)
		// fmt.Println(len(sli))
		for i := 1; i < len(sli); i += 2 {
			// 组装proxy
			py_url := string(sli[i-1][1]) + ":" + string(sli[i][1])
			if log {
				fmt.Println(time.Now(), fmt.Sprintf("[New Proxy]: %s", py_url))
			}
			py := Proxy{string(sli[i-1][1]), string(sli[i][1]), ValidProxyChan, time.Second}
			WorkerChan <- py
		}
		// 一页休息一下
		time.Sleep(delay)
	}
}

func getValidProxy(WorkerChan <-chan Proxy, ValidProxyChan chan<- Proxy, WG *sync.WaitGroup, timeout time.Duration, log bool) {
	defer close(ValidProxyChan)
	for py := range WorkerChan {
		WG.Add(1)
		go py.doTest(WG, timeout, log)
	}
	WG.Wait()
}

func processValidProxy(ValidProxyChan <-chan Proxy) (ValidProxy ProxySlice) {
	for py := range ValidProxyChan {
		ValidProxy = append(ValidProxy, py)
	}
	return
}

// 获取有效的代理ip，返回代理ip结构体切片
//
//	[pages]: 爬取的源代理ip页数，一页有20个可测试的代理ip，估计有三分之一会有效
//	[timeout]: 测试代理ip的超时时间，一般设为 5秒 左右，使用 time.Second*秒数 传递
//	[delay]: 爬取pages页的中间延迟，一般设为 500毫秒 左右，使用 time.Millisecond*毫秒数 传递
//	[log]: 是否显示爬取和测试的中间日志，默认为false
//	return: 有效代理ip的结构体切片
func GetValidProxy(pages int, timeout time.Duration, delay time.Duration, log bool) (ValidProxy ProxySlice) {
	WorkerChan := make(chan Proxy, chansize)                         // 准备执行测试方法的proxy管道
	ValidProxyChan := make(chan Proxy)                               // 测试合格的proxy管道
	var WG sync.WaitGroup                                            // 用于同步完成工作的WaitGroup
	go getOriginProxy(WorkerChan, ValidProxyChan, pages, delay, log) // 获取原始的proxy，送入WorkerChan用于测试
	go getValidProxy(WorkerChan, ValidProxyChan, &WG, timeout, log)  // 测试各proxy，获取有效proxy
	ValidProxy = processValidProxy(ValidProxyChan)                   // 将有效proxy打包返回
	sort.Sort(ValidProxy)
	return
}

// func main() {
// 	WorkerChan := make(chan Proxy, CHANSIZE)          // 准备执行测试方法的proxy管道
// 	ValidProxyChan := make(chan Proxy)                // 测试合格的proxy管道
// 	var WG sync.WaitGroup                             // 用于同步完成工作的WaitGroup
// 	go GetOriginProxy(WorkerChan, ValidProxyChan)     // 获取原始的proxy，送入WorkerChan用于测试
// 	go GetValidProxy(WorkerChan, ValidProxyChan, &WG) // 测试各proxy，获取有效proxy
// 	ValidProxy := ProcessValidProxy(ValidProxyChan)   // 将有效proxy打包返回
// 	fmt.Println("-----------------------------------")
// 	for _, v := range ValidProxy {
// 		fmt.Println(v)
// 	}
// }
